/*
 * Phrase_connector.cpp
 *
 *  Created on: Jun 10, 2011
 *      Author: Mario Hock
 */

/*
 * PROTOCOL: (XXX dht änderung nochnicht berücksichtigt..)
 *
 * User A                                 User B
 *    |                                      |
 *    |                                     meet(phrase)
 *   ...                                    ...  (no answer)
 *    |                                      |
 *  meet(phrase) \                           |
 *                \_______________________ receive_connection_phrase(phrase, A, id)
 *                                           |
 *                                         accept
 *                                        /
 *  invitation_accepted(phrase, B, id)___/
 *
 */


#include "Phrase_connector.h"
#include "UI_multiplexer.h"
#include <boost/algorithm/string.hpp>
#include <locale>

#include <boost/foreach.hpp>

#define CONNECTOR_SERVICE_ID 223
#define DHT_SERVICE_ID 222

//** Constructors / Destructors **//
Phrase_connector::Phrase_connector(Userdatabase& users, ariba::Node& node, Transport& trans) :
    serviceID(ariba::ServiceID(CONNECTOR_SERVICE_ID)),
    users(users),
    node(node),
    trans(trans),
    dht(ariba::ServiceID(DHT_SERVICE_ID), &node),
    pending_invitations(""),
    waiting(false)
{
    this->node.bind(this, serviceID);
    dht.add_listener(this);
}

Phrase_connector::~Phrase_connector()
{
    this->node.unbind(this, serviceID);
    dht.remove_listener(this);
}



/** called when user typed a connection phrase **/
void Phrase_connector::meet(string phrase)
{
    // abort pending meets 
    if ( pending_invitations != "" )
    {
        dht.stop_meet(pending_invitations, users.get_myself()->get_name());
        dht.remove(pending_invitations, users.get_myself()->get_name());
        
        UI_multiplexer::instance().write_info("MEET: Abort Meet with phrase '" + pending_invitations + "'");
    }
    
    
    // simplify meet phrase (--> to lowercase)
    // TODO anständige lowercase funktion
    pending_invitations = boost::algorithm::to_lower_copy(phrase);
    boost::algorithm::replace_all(pending_invitations, "Ä", "ä");
    boost::algorithm::replace_all(pending_invitations, "Ö", "ä");
    boost::algorithm::replace_all(pending_invitations, "Ü", "ä");
    
    waiting = false;
    
    UI_multiplexer::instance().write_info("MEET: looking for '" + pending_invitations + "' in the DHT...");
    

    // * periodic put & get in the dht *
    dht.meet(pending_invitations, users.get_myself()->get_name(), 10*60);
}



void Phrase_connector::handle_dht_answer(const std::string& key, const std::vector<std::string>& values)
{
    bool found = false;
    
//    UI_multiplexer::instance().write_info("DHT: Got answer for key: " + key);  // XXX debug output

    if ( pending_invitations == key )
    {
        BOOST_FOREACH(string name, values)
        {
//            UI_multiplexer::instance().write_info("DHT: ---> " + name);  // XXX debug output
            
            // BRANCH: get name of other user -> connect
            if ( name != users.get_myself()->get_name() )
            {
                found = true;

                // stop meeting
                cout << ">>>>>>> Phrase_connector --> stop meet!!" << endl; // XXX
                dht.stop_meet(key, users.get_myself()->get_name());
                
                UI_multiplexer::instance().write_info("MEET: Found " + name + " at (" + key + ")");

                // send hello
                MeetMessage ans(MeetMessage::Hello, key, users.get_myself()->get_name());
                node.sendMessage(ans, ariba::Name(name).toNodeId(), serviceID);
            }
        }
        
        
        if ( ! found )
        {
            if ( ! waiting )
            {
                UI_multiplexer::instance().write_info("MEET: We're first at this meeting point (" + key + ")");
                waiting = true;
            }
            else
            {
                UI_multiplexer::instance().write_info("MEET: Waiting... at (" + key + ")");
            }
        }
    }
}



void Phrase_connector::onMessage(const ariba::DataMessage& msg, const ariba::NodeID& source,
        const ariba::LinkID& lnk)
{
    MeetMessage* m = msg.getMessage()->convert<MeetMessage>();
    const string& phrase = m->get_connection_phrase();

//    // XXX debug
//    UI_multiplexer::instance().write_info("MEET: Incoming message from: " + m->get_name() + "  (" + phrase + ")");
    
    if ( pending_invitations == phrase )
    {
        switch (m->getType())
        {
            // A ---> B
            case MeetMessage::Hello:
            {
                // stop meeting
                cout << ">>>>>>> Phrase_connector --> stop meet!!" << endl; // XXX
                dht.stop_meet(phrase, users.get_myself()->get_name());
                
                UI_multiplexer::instance().write_info("MEET: Found " + m->get_name() + " at (" + phrase + ")");

                
                // TODO NONCE instead of phrase ??
                
                MeetMessage ans(MeetMessage::Invite, phrase, users.get_myself()->get_name());
                node.sendMessage(ans, source, serviceID);
                
                break;
            }
            // A <--- B
            case MeetMessage::Invite:
            {
                dht.remove(pending_invitations, users.get_myself()->get_name());
                
                UserPtr u = users.createUser(m->get_name());
                UI_multiplexer::instance().write_to_user("[USER_UP] User added: " + m->get_name());

                MeetMessage ans(MeetMessage::Accept, phrase, users.get_myself()->get_name());
                node.sendMessage(ans, source, serviceID);
                
                pending_invitations = "";
                
                break;
            }
            // A ---> B
            case MeetMessage::Accept:
            {
                dht.remove(pending_invitations, users.get_myself()->get_name());
                
                UserPtr u = users.createUser(m->get_name());
                UI_multiplexer::instance().write_to_user("[USER_UP] User added: " + m->get_name());
                
                pending_invitations = "";
                
                // connect to the new user
                trans.establish_connection(u);
                
                break;
            }

            default:
                UI_multiplexer::instance().write_warn("Got Meet Message with wrong type.");
                
                break;
        }
    }
    else
    {
        UI_multiplexer::instance().write_warn("Got Meet Message with a phrase we don't know about.");
    }

    delete m;
}
